{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "import subprocess\n",
        "import sys\n",
        "\n",
        "def install_packages():\n",
        "    packages = [\n",
        "        \"langgraph\",\n",
        "        \"langchain\",\n",
        "        \"langchain-anthropic\",\n",
        "        \"langchain-community\",\n",
        "        \"requests\",\n",
        "        \"python-dotenv\",\n",
        "        \"duckduckgo-search\"\n",
        "    ]\n",
        "\n",
        "    for package in packages:\n",
        "        try:\n",
        "            subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", package, \"-q\"])\n",
        "            print(f\"✓ Installed {package}\")\n",
        "        except subprocess.CalledProcessError:\n",
        "            print(f\"✗ Failed to install {package}\")\n",
        "\n",
        "print(\"Installing required packages...\")\n",
        "install_packages()\n",
        "print(\"Installation complete!\\n\")"
      ],
      "metadata": {
        "id": "ojhopXxMzkIm"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "import json\n",
        "import math\n",
        "import requests\n",
        "from typing import Dict, List, Any, Annotated, TypedDict\n",
        "from datetime import datetime\n",
        "import operator\n",
        "\n",
        "from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage\n",
        "from langchain_core.tools import tool\n",
        "from langchain_anthropic import ChatAnthropic\n",
        "from langgraph.graph import StateGraph, START, END\n",
        "from langgraph.prebuilt import ToolNode\n",
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "from duckduckgo_search import DDGS"
      ],
      "metadata": {
        "id": "kVNNa7GzzmW-"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "os.environ[\"ANTHROPIC_API_KEY\"] = \"Use Your API Key Here\"\n",
        "\n",
        "ANTHROPIC_API_KEY = os.getenv(\"ANTHROPIC_API_KEY\")"
      ],
      "metadata": {
        "id": "bcmEtjBCzn42"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "from typing import TypedDict\n",
        "\n",
        "class AgentState(TypedDict):\n",
        "    messages: Annotated[List[BaseMessage], operator.add]\n",
        "\n",
        "@tool\n",
        "def calculator(expression: str) -> str:\n",
        "    \"\"\"\n",
        "    Perform mathematical calculations. Supports basic arithmetic, trigonometry, and more.\n",
        "\n",
        "    Args:\n",
        "        expression: Mathematical expression as a string (e.g., \"2 + 3 * 4\", \"sin(3.14159/2)\")\n",
        "\n",
        "    Returns:\n",
        "        Result of the calculation as a string\n",
        "    \"\"\"\n",
        "    try:\n",
        "        allowed_names = {\n",
        "            'abs': abs, 'round': round, 'min': min, 'max': max,\n",
        "            'sum': sum, 'pow': pow, 'sqrt': math.sqrt,\n",
        "            'sin': math.sin, 'cos': math.cos, 'tan': math.tan,\n",
        "            'log': math.log, 'log10': math.log10, 'exp': math.exp,\n",
        "            'pi': math.pi, 'e': math.e\n",
        "        }\n",
        "\n",
        "        expression = expression.replace('^', '**')\n",
        "\n",
        "        result = eval(expression, {\"__builtins__\": {}}, allowed_names)\n",
        "        return f\"Result: {result}\"\n",
        "    except Exception as e:\n",
        "        return f\"Error in calculation: {str(e)}\""
      ],
      "metadata": {
        "id": "UILkpo2KzvlW"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "@tool\n",
        "def web_search(query: str, num_results: int = 3) -> str:\n",
        "    \"\"\"\n",
        "    Search the web for information using DuckDuckGo.\n",
        "\n",
        "    Args:\n",
        "        query: Search query string\n",
        "        num_results: Number of results to return (default: 3, max: 10)\n",
        "\n",
        "    Returns:\n",
        "        Search results as formatted string\n",
        "    \"\"\"\n",
        "    try:\n",
        "        num_results = min(max(num_results, 1), 10)\n",
        "\n",
        "        with DDGS() as ddgs:\n",
        "            results = list(ddgs.text(query, max_results=num_results))\n",
        "\n",
        "        if not results:\n",
        "            return f\"No search results found for: {query}\"\n",
        "\n",
        "        formatted_results = f\"Search results for '{query}':\\n\\n\"\n",
        "        for i, result in enumerate(results, 1):\n",
        "            formatted_results += f\"{i}. **{result['title']}**\\n\"\n",
        "            formatted_results += f\"   {result['body']}\\n\"\n",
        "            formatted_results += f\"   Source: {result['href']}\\n\\n\"\n",
        "\n",
        "        return formatted_results\n",
        "    except Exception as e:\n",
        "        return f\"Error performing web search: {str(e)}\""
      ],
      "metadata": {
        "id": "euTJoBeqz0HV"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "@tool\n",
        "def weather_info(city: str) -> str:\n",
        "    \"\"\"\n",
        "    Get current weather information for a city using OpenWeatherMap API.\n",
        "    Note: This is a mock implementation for demo purposes.\n",
        "\n",
        "    Args:\n",
        "        city: Name of the city\n",
        "\n",
        "    Returns:\n",
        "        Weather information as a string\n",
        "    \"\"\"\n",
        "    mock_weather = {\n",
        "        \"new york\": {\"temp\": 22, \"condition\": \"Partly Cloudy\", \"humidity\": 65},\n",
        "        \"london\": {\"temp\": 15, \"condition\": \"Rainy\", \"humidity\": 80},\n",
        "        \"tokyo\": {\"temp\": 28, \"condition\": \"Sunny\", \"humidity\": 70},\n",
        "        \"paris\": {\"temp\": 18, \"condition\": \"Overcast\", \"humidity\": 75}\n",
        "    }\n",
        "\n",
        "    city_lower = city.lower()\n",
        "    if city_lower in mock_weather:\n",
        "        weather = mock_weather[city_lower]\n",
        "        return f\"Weather in {city}:\\n\" \\\n",
        "               f\"Temperature: {weather['temp']}°C\\n\" \\\n",
        "               f\"Condition: {weather['condition']}\\n\" \\\n",
        "               f\"Humidity: {weather['humidity']}%\"\n",
        "    else:\n",
        "        return f\"Weather data not available for {city}. (This is a demo with limited cities: New York, London, Tokyo, Paris)\""
      ],
      "metadata": {
        "id": "v2Y_KA0pz2Q1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "@tool\n",
        "def text_analyzer(text: str) -> str:\n",
        "    \"\"\"\n",
        "    Analyze text and provide statistics like word count, character count, etc.\n",
        "\n",
        "    Args:\n",
        "        text: Text to analyze\n",
        "\n",
        "    Returns:\n",
        "        Text analysis results\n",
        "    \"\"\"\n",
        "    if not text.strip():\n",
        "        return \"Please provide text to analyze.\"\n",
        "\n",
        "    words = text.split()\n",
        "    sentences = text.split('.') + text.split('!') + text.split('?')\n",
        "    sentences = [s.strip() for s in sentences if s.strip()]\n",
        "\n",
        "    analysis = f\"Text Analysis Results:\\n\"\n",
        "    analysis += f\"• Characters (with spaces): {len(text)}\\n\"\n",
        "    analysis += f\"• Characters (without spaces): {len(text.replace(' ', ''))}\\n\"\n",
        "    analysis += f\"• Words: {len(words)}\\n\"\n",
        "    analysis += f\"• Sentences: {len(sentences)}\\n\"\n",
        "    analysis += f\"• Average words per sentence: {len(words) / max(len(sentences), 1):.1f}\\n\"\n",
        "    analysis += f\"• Most common word: {max(set(words), key=words.count) if words else 'N/A'}\"\n",
        "\n",
        "    return analysis"
      ],
      "metadata": {
        "id": "IV4vqT2Lz5w1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "@tool\n",
        "def current_time() -> str:\n",
        "    \"\"\"\n",
        "    Get the current date and time.\n",
        "\n",
        "    Returns:\n",
        "        Current date and time as a formatted string\n",
        "    \"\"\"\n",
        "    now = datetime.now()\n",
        "    return f\"Current date and time: {now.strftime('%Y-%m-%d %H:%M:%S')}\""
      ],
      "metadata": {
        "id": "O9hqLJoDz7Yl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "tools = [calculator, web_search, weather_info, text_analyzer, current_time]\n",
        "\n",
        "def create_llm():\n",
        "    if ANTHROPIC_API_KEY:\n",
        "        return ChatAnthropic(\n",
        "            model=\"claude-3-haiku-20240307\",\n",
        "            temperature=0.1,\n",
        "            max_tokens=1024\n",
        "        )\n",
        "    else:\n",
        "        class MockLLM:\n",
        "            def invoke(self, messages):\n",
        "                last_message = messages[-1].content if messages else \"\"\n",
        "\n",
        "                if any(word in last_message.lower() for word in ['calculate', 'math', '+', '-', '*', '/', 'sqrt', 'sin', 'cos']):\n",
        "                    import re\n",
        "                    numbers = re.findall(r'[\\d\\+\\-\\*/\\.\\(\\)\\s\\w]+', last_message)\n",
        "                    expr = numbers[0] if numbers else \"2+2\"\n",
        "                    return AIMessage(content=\"I'll help you with that calculation.\",\n",
        "                                   tool_calls=[{\"name\": \"calculator\", \"args\": {\"expression\": expr.strip()}, \"id\": \"calc1\"}])\n",
        "                elif any(word in last_message.lower() for word in ['search', 'find', 'look up', 'information about']):\n",
        "                    query = last_message.replace('search for', '').replace('find', '').replace('look up', '').strip()\n",
        "                    if not query or len(query) < 3:\n",
        "                        query = \"python programming\"\n",
        "                    return AIMessage(content=\"I'll search for that information.\",\n",
        "                                   tool_calls=[{\"name\": \"web_search\", \"args\": {\"query\": query}, \"id\": \"search1\"}])\n",
        "                elif any(word in last_message.lower() for word in ['weather', 'temperature']):\n",
        "                    city = \"New York\"\n",
        "                    words = last_message.lower().split()\n",
        "                    for i, word in enumerate(words):\n",
        "                        if word == 'in' and i + 1 < len(words):\n",
        "                            city = words[i + 1].title()\n",
        "                            break\n",
        "                    return AIMessage(content=\"I'll get the weather information.\",\n",
        "                                   tool_calls=[{\"name\": \"weather_info\", \"args\": {\"city\": city}, \"id\": \"weather1\"}])\n",
        "                elif any(word in last_message.lower() for word in ['time', 'date']):\n",
        "                    return AIMessage(content=\"I'll get the current time.\",\n",
        "                                   tool_calls=[{\"name\": \"current_time\", \"args\": {}, \"id\": \"time1\"}])\n",
        "                elif any(word in last_message.lower() for word in ['analyze', 'analysis']):\n",
        "                    text = last_message.replace('analyze this text:', '').replace('analyze', '').strip()\n",
        "                    if not text:\n",
        "                        text = \"Sample text for analysis\"\n",
        "                    return AIMessage(content=\"I'll analyze that text for you.\",\n",
        "                                   tool_calls=[{\"name\": \"text_analyzer\", \"args\": {\"text\": text}, \"id\": \"analyze1\"}])\n",
        "                else:\n",
        "                    return AIMessage(content=\"Hello! I'm a multi-tool agent powered by Claude. I can help with:\\n• Mathematical calculations\\n• Web searches\\n• Weather information\\n• Text analysis\\n• Current time/date\\n\\nWhat would you like me to help you with?\")\n",
        "\n",
        "            def bind_tools(self, tools):\n",
        "                return self\n",
        "\n",
        "        print(\"⚠️  Note: Using mock LLM for demo. Add your ANTHROPIC_API_KEY for full functionality.\")\n",
        "        return MockLLM()\n",
        "\n",
        "llm = create_llm()\n",
        "llm_with_tools = llm.bind_tools(tools)"
      ],
      "metadata": {
        "id": "zfE4b5d60CMm"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def agent_node(state: AgentState) -> Dict[str, Any]:\n",
        "    \"\"\"Main agent node that processes messages and decides on tool usage.\"\"\"\n",
        "    messages = state[\"messages\"]\n",
        "    response = llm_with_tools.invoke(messages)\n",
        "    return {\"messages\": [response]}\n",
        "\n",
        "def should_continue(state: AgentState) -> str:\n",
        "    \"\"\"Determine whether to continue with tool calls or end.\"\"\"\n",
        "    last_message = state[\"messages\"][-1]\n",
        "    if hasattr(last_message, 'tool_calls') and last_message.tool_calls:\n",
        "        return \"tools\"\n",
        "    return END"
      ],
      "metadata": {
        "id": "2TPO8nI40EP-"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def create_agent_graph():\n",
        "    tool_node = ToolNode(tools)\n",
        "\n",
        "    workflow = StateGraph(AgentState)\n",
        "\n",
        "    workflow.add_node(\"agent\", agent_node)\n",
        "    workflow.add_node(\"tools\", tool_node)\n",
        "\n",
        "    workflow.add_edge(START, \"agent\")\n",
        "    workflow.add_conditional_edges(\"agent\", should_continue, {\"tools\": \"tools\", END: END})\n",
        "    workflow.add_edge(\"tools\", \"agent\")\n",
        "\n",
        "    memory = MemorySaver()\n",
        "\n",
        "    app = workflow.compile(checkpointer=memory)\n",
        "\n",
        "    return app\n",
        "\n",
        "print(\"Creating LangGraph Multi-Tool Agent...\")\n",
        "agent = create_agent_graph()\n",
        "print(\"✓ Agent created successfully!\\n\")"
      ],
      "metadata": {
        "id": "GbVXP-JR0KSt"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def test_agent():\n",
        "    \"\"\"Test the agent with various queries.\"\"\"\n",
        "    config = {\"configurable\": {\"thread_id\": \"test-thread\"}}\n",
        "\n",
        "    test_queries = [\n",
        "        \"What's 15 * 7 + 23?\",\n",
        "        \"Search for information about Python programming\",\n",
        "        \"What's the weather like in Tokyo?\",\n",
        "        \"What time is it?\",\n",
        "        \"Analyze this text: 'LangGraph is an amazing framework for building AI agents.'\"\n",
        "    ]\n",
        "\n",
        "    print(\"🧪 Testing the agent with sample queries...\\n\")\n",
        "\n",
        "    for i, query in enumerate(test_queries, 1):\n",
        "        print(f\"Query {i}: {query}\")\n",
        "        print(\"-\" * 50)\n",
        "\n",
        "        try:\n",
        "            response = agent.invoke(\n",
        "                {\"messages\": [HumanMessage(content=query)]},\n",
        "                config=config\n",
        "            )\n",
        "\n",
        "            last_message = response[\"messages\"][-1]\n",
        "            print(f\"Response: {last_message.content}\\n\")\n",
        "\n",
        "        except Exception as e:\n",
        "            print(f\"Error: {str(e)}\\n\")"
      ],
      "metadata": {
        "id": "h4ZknozS0OoO"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def chat_with_agent():\n",
        "    \"\"\"Interactive chat function.\"\"\"\n",
        "    config = {\"configurable\": {\"thread_id\": \"interactive-thread\"}}\n",
        "\n",
        "    print(\"🤖 Multi-Tool Agent Chat\")\n",
        "    print(\"Available tools: Calculator, Web Search, Weather Info, Text Analyzer, Current Time\")\n",
        "    print(\"Type 'quit' to exit, 'help' for available commands\\n\")\n",
        "\n",
        "    while True:\n",
        "        try:\n",
        "            user_input = input(\"You: \").strip()\n",
        "\n",
        "            if user_input.lower() in ['quit', 'exit', 'q']:\n",
        "                print(\"Goodbye!\")\n",
        "                break\n",
        "            elif user_input.lower() == 'help':\n",
        "                print(\"\\nAvailable commands:\")\n",
        "                print(\"• Calculator: 'Calculate 15 * 7 + 23' or 'What's sin(pi/2)?'\")\n",
        "                print(\"• Web Search: 'Search for Python tutorials' or 'Find information about AI'\")\n",
        "                print(\"• Weather: 'Weather in Tokyo' or 'What's the temperature in London?'\")\n",
        "                print(\"• Text Analysis: 'Analyze this text: [your text]'\")\n",
        "                print(\"• Current Time: 'What time is it?' or 'Current date'\")\n",
        "                print(\"• quit: Exit the chat\\n\")\n",
        "                continue\n",
        "            elif not user_input:\n",
        "                continue\n",
        "\n",
        "            response = agent.invoke(\n",
        "                {\"messages\": [HumanMessage(content=user_input)]},\n",
        "                config=config\n",
        "            )\n",
        "\n",
        "            last_message = response[\"messages\"][-1]\n",
        "            print(f\"Agent: {last_message.content}\\n\")\n",
        "\n",
        "        except KeyboardInterrupt:\n",
        "            print(\"\\nGoodbye!\")\n",
        "            break\n",
        "        except Exception as e:\n",
        "            print(f\"Error: {str(e)}\\n\")"
      ],
      "metadata": {
        "id": "mVlzGSST0UQl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "zQ76rlnFwIuE",
        "outputId": "4912a6ee-a52a-4a94-897b-093bce64626e"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Installing required packages...\n",
            "✓ Installed langgraph\n",
            "✓ Installed langchain\n",
            "✓ Installed langchain-anthropic\n",
            "✓ Installed langchain-community\n",
            "✓ Installed requests\n",
            "✓ Installed python-dotenv\n",
            "✓ Installed duckduckgo-search\n",
            "Installation complete!\n",
            "\n",
            "Creating LangGraph Multi-Tool Agent...\n",
            "✓ Agent created successfully!\n",
            "\n",
            "🧪 Testing the agent with sample queries...\n",
            "\n",
            "Query 1: What's 15 * 7 + 23?\n",
            "--------------------------------------------------\n",
            "Response: So, 15 * 7 + 23 = 128.\n",
            "\n",
            "Query 2: Search for information about Python programming\n",
            "--------------------------------------------------\n",
            "Response: These search results provide a good overview of Python programming, including the official Python website, Wikipedia article, and a popular tutorial from W3Schools. Let me know if you need any other information!\n",
            "\n",
            "Query 3: What's the weather like in Tokyo?\n",
            "--------------------------------------------------\n",
            "Response: Based on the weather information, it looks like the weather in Tokyo is sunny with a temperature of 28°C (around 82°F) and 70% humidity.\n",
            "\n",
            "Query 4: What time is it?\n",
            "--------------------------------------------------\n",
            "Response: The current date and time is 2025-05-22 20:57:38.\n",
            "\n",
            "Query 5: Analyze this text: 'LangGraph is an amazing framework for building AI agents.'\n",
            "--------------------------------------------------\n",
            "Response: The key insights from the text analysis are:\n",
            "- The text has 57 characters (including spaces) and 49 characters without spaces.\n",
            "- It contains 9 words across 3 sentences, with an average of 3 words per sentence.\n",
            "- The most common word in the text is \"building\".\n",
            "\n",
            "Overall, this is a short, concise sentence describing LangGraph as an amazing framework for building AI agents.\n",
            "\n",
            "============================================================\n",
            "🎉 LangGraph Multi-Tool Agent is ready!\n",
            "============================================================\n",
            "🤖 Multi-Tool Agent Chat\n",
            "Available tools: Calculator, Web Search, Weather Info, Text Analyzer, Current Time\n",
            "Type 'quit' to exit, 'help' for available commands\n",
            "\n",
            "You: exit\n",
            "Goodbye!\n",
            "\n",
            "============================================================\n",
            "🔧 Usage Instructions:\n",
            "1. Add your ANTHROPIC_API_KEY to use Claude model\n",
            "   os.environ['ANTHROPIC_API_KEY'] = 'your-anthropic-api-key'\n",
            "2. Run quick_demo() for a quick demonstration\n",
            "3. Run chat_with_agent() for interactive chat\n",
            "4. The agent supports: calculations, web search, weather, text analysis, and time\n",
            "5. Example: 'Calculate 15*7+23' or 'Search for Python tutorials'\n",
            "============================================================\n"
          ]
        }
      ],
      "source": [
        "if __name__ == \"__main__\":\n",
        "    test_agent()\n",
        "\n",
        "    print(\"=\" * 60)\n",
        "    print(\"🎉 LangGraph Multi-Tool Agent is ready!\")\n",
        "    print(\"=\" * 60)\n",
        "\n",
        "    chat_with_agent()\n",
        "\n",
        "def quick_demo():\n",
        "    \"\"\"Quick demonstration of agent capabilities.\"\"\"\n",
        "    config = {\"configurable\": {\"thread_id\": \"demo\"}}\n",
        "\n",
        "    demos = [\n",
        "        (\"Math\", \"Calculate the square root of 144 plus 5 times 3\"),\n",
        "        (\"Search\", \"Find recent news about artificial intelligence\"),\n",
        "        (\"Time\", \"What's the current date and time?\")\n",
        "    ]\n",
        "\n",
        "    print(\"🚀 Quick Demo of Agent Capabilities\\n\")\n",
        "\n",
        "    for category, query in demos:\n",
        "        print(f\"[{category}] Query: {query}\")\n",
        "        try:\n",
        "            response = agent.invoke(\n",
        "                {\"messages\": [HumanMessage(content=query)]},\n",
        "                config=config\n",
        "            )\n",
        "            print(f\"Response: {response['messages'][-1].content}\\n\")\n",
        "        except Exception as e:\n",
        "            print(f\"Error: {str(e)}\\n\")\n",
        "\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"🔧 Usage Instructions:\")\n",
        "print(\"1. Add your ANTHROPIC_API_KEY to use Claude model\")\n",
        "print(\"   os.environ['ANTHROPIC_API_KEY'] = 'your-anthropic-api-key'\")\n",
        "print(\"2. Run quick_demo() for a quick demonstration\")\n",
        "print(\"3. Run chat_with_agent() for interactive chat\")\n",
        "print(\"4. The agent supports: calculations, web search, weather, text analysis, and time\")\n",
        "print(\"5. Example: 'Calculate 15*7+23' or 'Search for Python tutorials'\")\n",
        "print(\"=\"*60)"
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "froVp5isziN3"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}